/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;

/**
 * OPParamsJS class is the JavaScript equivalent of {@link OPParams} class. It's a JavaScriptObject, that is a real javascript object mapped to Java thanks to GWT overlay absolutely fantastic
 * technique. It is necessary to use a real javascript object (a GWT JavaScriptObject) because passing "java" object between differents GWT modules (as parts are) gives very strange results, thanks to
 * GWT compiler optimisations.
 * <p>
 * There are helpers methods to transform an OPParamsJS object into a {@link OPParams} one, and vice-versa.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 01 January 2010
 */
public final class OPParamsJS extends JavaScriptObject {

    /**
     * Protected constructor, necessary because it's a JavaScriptObject.
     */
    protected OPParamsJS() {
    }

    /**
     * Create and return an OPParamsJS object from an OPParams one.
     * 
     * @param params
     *            the OPParams object to use to build the OPParamsJS. Shouldn't be null.
     * @return an OPParamsJS corresponding to the given OPParams. Cannot be null.
     */
    public final static OPParamsJS buildFromOPParams(final OPParams params) {
        assert params != null : "parameter params shouldn't be null";

        final OPParamsJS paramsJS = createEmptyOPParamsJS();
        for (String paramName : params.getNames()) {
            final String paramValue = params.getParamValue(paramName);
            paramsJS.addParam(paramName, paramValue);
        }

        return paramsJS;
    }

    /**
     * Create an empty OPParamsJS object.
     * 
     * @return an empty OPParamsJS object.
     */
    private final static native OPParamsJS createEmptyOPParamsJS() /*-{
        var p = {};
        return p;
    }-*/;

    /**
     * Add the given parameter. If the given parameter was already added, then just change its value.
     * 
     * @param paramName
     *            the parameter name.
     * @param paramValue
     *            the parameter value.
     */
    private final native void addParam(final String paramName, final String paramValue) /*-{
        this[paramName] = paramValue;
    }-*/;

    /**
     * Returns an OPParams object corresponding to this OPParamsJS object.
     * 
     * @return an OPParams object corresponding to this OPParamsJS object. Cannot be null.
     */
    public final OPParams toOPParams() {
        final OPParams params = new OPParams();

        final JsArrayString names = getNames();
        for (int i = 0; i < names.length(); i++) {
            final String name = names.get(i);
            final String value = getParamValue(name);
            params.addParam(name, value);
        }

        return params;
    }

    /**
     * Returns a native javascript array containing all params names.
     * 
     * @return a native javascript array containing all params names. Can be empty, cannot be null.
     */
    private final native JsArrayString getNames() /*-{
        var names = new Array();
        var count = 0;

        for ( var p in this) {
            if (this.hasOwnProperty(p)) {
                names[count] = p;
                ++count;
            }
        }

        return names;
    }-*/;

    /**
     * Returns the param value for the given param name. Return null if there is no param with such a name.
     * 
     * @param paramName
     *            name of the searched parameter.
     * @return the value of the searched param, or null if there is no param with such a name.
     */
    public final native String getParamValue(final String paramName) /*-{
        if (typeof this[paramName] === undefined) {
            return null;
        }

        return this[paramName];
    }-*/;
    
    /**
     * Returns a human-readable description of this params content. Useful for debugging. Unfortunately, cannot override toString as it is final in
     * JavaScriptObject.
     * 
     * @return a human-readable description of the params, useful for debugging.
     */
    public final String getDescription() {
        String description = "[";
        final JsArrayString names = getNames();
        for (int i = 0; i < names.length(); i++) {
            String name = names.get(i);
            String value = getParamValue(name);
            if (i != 0) {
                description += ", ";
            }
            description += "(" + name + ":" + value + ")";
        }
        return description + "]";
    }

}
